package com.bank.manage.advice;

import com.alibaba.fastjson2.JSON;
import com.bank.manage.constance.BankConstance;
import com.bank.manage.exception.BankException;
import com.bank.manage.model.BankCheckRecords;
import com.bank.manage.service.BankCheckRecordsService;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.validation.ConstraintViolationException;
import jakarta.validation.Path;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.gavin.core.attribute.YmlSafetyAttribute;
import org.gavin.core.constant.CommonConst;
import org.gavin.core.domain.R;
import org.gavin.core.enums.CommonStatus;
import org.gavin.core.exception.ParamException;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;

import java.time.LocalDateTime;

/**
 * ResponseExceptionAdvice
 *
 * @author grl
 * @date 2024/1/4
 */
@Slf4j
@ResponseBody
@ControllerAdvice
@RequiredArgsConstructor
public class BankResponseExceptionAdvice {

    private final HttpServletRequest request;
    private final BankCheckRecordsService checkRecordsService;
    private final YmlSafetyAttribute ymlSafetyAttribute;

    @ExceptionHandler(value = ParamException.class)
    public R paramExceptionHandler(ParamException e) {
        addCheckRecord(e.getMsg(),R.fail(e.getCode(), e.getMessage()));
        log.error("Param Exception: {} {} {}", e.getModule(), e.getMsg(), e.getArgs(), e);
        return R.fail(e.getCode(), e.getMessage());
    }

    @ExceptionHandler(value = BankException.class)
    public R bankExceptionHandler(BankException e) {
        addCheckRecord(e,R.fail(e.getCode(), e.getMessage()));
        log.error("Param Exception: {} {} {} {}", e.getModule(), e.getMsg(), e.getArgs(), JSON.toJSONString(e.getRecords()), e);
        return R.fail(e.getCode(), e.getMessage());
    }

    /**
     * 对方法参数校验异常处理方法(前端提交的方式为json格式出现异常时会被该异常类处理)
     * json格式提交时，spring会采用json数据的数据转换器进行处理（进行参数校验时错误是抛出MethodArgumentNotValidException异常）
     */
    @ExceptionHandler(value = MethodArgumentNotValidException.class)
    public R validExceptionHandler(MethodArgumentNotValidException e) {
        log.error("Param Exception:", e);
        FieldError fieldError = e.getBindingResult().getFieldError();
        String msg = null;
        if (fieldError != null) {
            msg = fieldError.getDefaultMessage();
        }
        addCheckRecord(msg,R.fail(CommonStatus.PARAM_ERROR.getCode(), msg));
        return R.fail(CommonStatus.PARAM_ERROR.getCode(), msg);
    }

    @ExceptionHandler(value = ConstraintViolationException.class)
    public R violationExceptionHandler(ConstraintViolationException e) {
        log.error("Param Exception:", e);
        String msg = e.getMessage();
        StringBuilder sb = new StringBuilder();
        e.getConstraintViolations().forEach(param->{
            String message = param.getMessage();
            Object invalidValue = param.getInvalidValue();
            Path propertyPath = param.getPropertyPath();
            if(propertyPath != null){
                sb.append(propertyPath).append(" ");
            }
            sb.append(message).append("传入的值为 ").append(invalidValue);
        });
        addCheckRecord(sb.toString(),R.fail(CommonStatus.PARAM_ERROR.getCode(), msg));
        return R.fail(CommonStatus.PARAM_ERROR.getCode(), msg);
    }

    private void addCheckRecord(String msg,R r) {
        String safetyKey = request.getHeader(CommonConst.SAFETY_KEY);
        if (StringUtils.isNotBlank(safetyKey) && isSafetyKey(safetyKey)) {
            String safetyType = request.getHeader(CommonConst.SAFETY_TYPE);
            LocalDateTime now = LocalDateTime.now();
            BankCheckRecords records = new BankCheckRecords();
            records.setVerificationWay(safetyType);
            records.setSafetyKey(safetyKey);
            records.setCreateTime(now);
            records.setCheckTime(now);
            records.setOperationResults(BankConstance.OPT_FAIL + " : " + msg);
            records.setVerificationResult(BankConstance.FAIL);
            records.setBankResponse(CommonConst.ZERO);
            records.setResponseJson(JSON.toJSONString(r));
            checkRecordsService.save(records);
        }
    }

    private void addCheckRecord(BankException e,R r) {
        String safetyKey = request.getHeader(CommonConst.SAFETY_KEY);
        if (StringUtils.isNotBlank(safetyKey) && isSafetyKey(safetyKey)) {
            String safetyType = request.getHeader(CommonConst.SAFETY_TYPE);
            BankCheckRecords records = e.getRecords();
            if (records == null) {
                records = new BankCheckRecords();
            }
            records.setVerificationWay(safetyType);
            LocalDateTime now = LocalDateTime.now();
            records.setCreateTime(now);
            records.setCheckTime(now);
            records.setId(null);
            records.setCheckTime(now);
            records.setBankResponse(CommonConst.ZERO);
            records.setResponseJson(JSON.toJSONString(r));
            checkRecordsService.save(records);
        }
    }

    private boolean isSafetyKey(String safetyKey) {
        return ymlSafetyAttribute.getMultiple().containsKey(safetyKey);
    }
}
